package dgm.driver.handler;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Charsets;
import dgm.exceptions.DegraphmalizerException;
import dgm.exceptions.WrappedException;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.http.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.StringWriter;
public class ExceptionHandler extends SimpleChannelHandler
{
// TODO use annotated POJO messages and inject the objectmapper
private static final ObjectMapper objectMapper = new ObjectMapper();
Logger log = LoggerFactory.getLogger(ExceptionHandler.class);
@Override
public final void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
{
final Channel c = ctx.getChannel();
final DegraphmalizerException ex = wrapException(e.getCause());
final String json = renderExceptionResponse(objectMapper, ex);
final HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, ex.httpStatusCode());
response.setContent(ChannelBuffers.copiedBuffer(json, Charsets.UTF_8));
logException(ex);
if(c.isOpen() && c.isWritable())
c.write(response).addListener(ChannelFutureListener.CLOSE);
}
// log according to severity
void logException(DegraphmalizerException ex)
{
switch (ex.severity())
{
case INFO:
log.info(ex.getMessage(), ex);
return;
case WARN:
log.warn(ex.getMessage(), ex);
return;
case ERROR:
default:
log.error(ex.getMessage(), ex);
return;
}
}
// wrap the exception if needed
private DegraphmalizerException wrapException(Throwable t)
{
if(DegraphmalizerException.class.isAssignableFrom(t.getClass()))
return (DegraphmalizerException)t;
return new WrappedException(t);
}
public static String renderExceptionResponse(ObjectMapper om, DegraphmalizerException ex) throws IOException
{
// TODO wrap in "data" element according to JSEND ?
// construct JSEND style JSON response http://labs.omniti.com/labs/jsend
final ObjectNode root = renderException(om, ex);
root.put("status", "error");
root.put("severity", ex.severity().name().toLowerCase());
// add optional cause
final Throwable cause = ex.getCause();
if(cause != null)
root.put("cause", renderException(om, cause));
final StringWriter sw = new StringWriter();
final JsonGenerator gen = new JsonFactory().createJsonGenerator(sw).useDefaultPrettyPrinter();
om.writeTree(gen, root);
return sw.toString();
}
public static ObjectNode renderException(ObjectMapper om, Throwable t)
{
final ArrayNode ss = om.createArrayNode();
for(StackTraceElement elt : t.getStackTrace())
ss.add(elt.toString());
final ObjectNode ex = om.createObjectNode();
ex.put("message", t.getMessage());
ex.put("class", t.getClass().getSimpleName());
ex.put("stacktrace", ss);
return ex;
}
}